package com.pucong.gitee.database.backup.business.service.util.cmdOs;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.pucong.gitee.database.backup.business.service.cover.ComputerServiceBeanConvert;
import com.pucong.gitee.database.backup.business.service.mapper.ServiceComputerMapper;
import com.pucong.gitee.database.backup.common.entity.service.SSHConnectInfo;
import com.pucong.gitee.database.backup.common.entity.service.ServiceComputer;
import com.pucong.gitee.database.backup.common.entity.service.ServiceComputerLoginDto;
import com.pucong.gitee.database.backup.common.entity.websock.LinuxWebsocketMsgDto;
import com.pucong.gitee.database.backup.common.selfenum.ServiceCommandType;
import com.pucong.gitee.database.backup.configuration.exception.BusinessException;
import com.pucong.gitee.database.backup.utils.ThreadPoolUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description: WebSSH业务逻辑实现
 * @Author: NoCortY
 * @Date: 2020/3/8
 */
@Service
public class WebSSHServiceImpl implements WebSSHService {
    //存放ssh连接信息的map
    private static Map<String, Object> sshMap = new ConcurrentHashMap<>();

    private Logger logger = LoggerFactory.getLogger(WebSSHServiceImpl.class);

    @Autowired
    private ServiceComputerMapper serviceComputerMapper;

    @Autowired
    private ComputerServiceBeanConvert computerServiceBeanConvert;

    /**
     * @param session
     * @Description: 初始化连接
     * @Param: [session]
     * @return: void
     * @Author: NoCortY
     * @Date: 2020/3/7
     */
    @Override
    public void initConnection(javax.websocket.Session session) {
        JSch jSch = new JSch();
        SSHConnectInfo sshConnectInfo = new SSHConnectInfo();
        sshConnectInfo.setJSch(jSch);
        sshConnectInfo.setSession(session);
        String uuid = session.getId();
        //将这个ssh连接信息放入map中
        sshMap.put(uuid, sshConnectInfo);
    }

    /**
     * @Description: 处理客户端发送的数据
     * @Param: [buffer, session]
     * @return: void
     * @Author: NoCortY
     * @Date: 2020/3/7
     */
    @Override
    public void recvHandle(String buffer, javax.websocket.Session session) {
        ObjectMapper objectMapper = new ObjectMapper();
        LinuxWebsocketMsgDto webSSHData = null;
        try {
            webSSHData = objectMapper.readValue(buffer, LinuxWebsocketMsgDto.class);
        } catch (IOException e) {
            logger.error("Json转换异常");
            logger.error("异常信息:{}", e);
            throw new BusinessException("传入参数错误");
        }
        ServiceComputer serviceComputer = serviceComputerMapper.selectById(webSSHData.getLinuxId());
        if (serviceComputer == null) {
            logger.error("传入参数错误");
            throw new BusinessException("传入参数错误");
        }
        ServiceComputerLoginDto serviceComputerLoginDto = computerServiceBeanConvert.do2loginDto(serviceComputer);
        String id = session.getId();
        if (webSSHData.getOperate() == ServiceCommandType.连接) {
            //找到刚才存储的ssh连接对象
            SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(id);
            //启动线程异步处理
            ThreadPoolUtils.executor(() -> {
                try {
                    connectToSSH(sshConnectInfo, serviceComputerLoginDto, session);
                } catch (Exception e) {
                    logger.error("webssh连接异常");
                    logger.error("异常信息:{}", e);
                    throw new BusinessException("webssh连接异常");
                } finally {
                    close(session);
                }
            });
        } else if (webSSHData.getOperate() == ServiceCommandType.执行指令) {
            String command = webSSHData.getCommand();
            SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(id);
            if (sshConnectInfo != null) {
                try {
                    transToSSH(sshConnectInfo.getChannel(), command);
                } catch (IOException e) {
                    logger.error("webssh连接异常");
                    logger.error("异常信息:{}", e.getMessage());
                    close(session);
                }
            }
        } else {
            logger.error("不支持的操作");
            close(session);
        }
    }

    @Override
    public void sendMessage(javax.websocket.Session session, byte[] buffer, int count) {
        synchronized (session) {
            try {
                session.getBasicRemote().sendText(new String(buffer, StandardCharsets.UTF_8));
            } catch (IOException e) {
                logger.error("发送消息失败，5次失败后停止发送，失败次数：" + count, e);
                this.sendMessage(session, buffer, count++);
            }
        }
    }

    @Override
    public void close(javax.websocket.Session session) {
        String id = session.getId();
        SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(id);
        if (sshConnectInfo != null) {
            //断开连接
            if (sshConnectInfo.getChannel() != null) {
                sshConnectInfo.getChannel().disconnect();
            }
            //map中移除
            sshMap.remove(id);
        }
    }

    /**
     * @Description: 使用jsch连接终端
     * @Param: [cloudSSH, webSSHData, webSocketSession]
     * @return: void
     * @Author: NoCortY
     * @Date: 2020/3/7
     */
    private void connectToSSH(SSHConnectInfo sshConnectInfo, ServiceComputerLoginDto dto, javax.websocket.Session socketSession) throws JSchException, IOException {
        Session session = null;
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        //获取jsch的会话
        session = sshConnectInfo.getJSch().getSession(dto.getUsername(), dto.getHost(), dto.getPort());
        session.setConfig(config);
        //设置密码
        session.setPassword(dto.getPassword());
        //连接  超时时间30s
        session.connect(30000);

        //开启shell通道
        Channel channel = session.openChannel("shell");

        //通道连接 超时时间3s
        channel.connect(3000);

        //设置channel
        sshConnectInfo.setChannel(channel);

        //转发消息
        transToSSH(channel, "\r");

        //读取终端返回的信息流
        InputStream inputStream = channel.getInputStream();
        try {
            //循环读取
            byte[] buffer = new byte[1024];
            int i = 0;
            //如果没有数据来，线程会一直阻塞在这个地方等待数据。
            while ((i = inputStream.read(buffer)) != -1) {
                sendMessage(socketSession, Arrays.copyOfRange(buffer, 0, i), 1);
            }

        } finally {
            //断开连接后关闭会话
            session.disconnect();
            channel.disconnect();
            if (inputStream != null) {
                inputStream.close();
            }
        }

    }

    /**
     * @Description: 将消息转发到终端
     * @Param: [channel, data]
     * @return: void
     * @Author: NoCortY
     * @Date: 2020/3/7
     */
    private void transToSSH(Channel channel, String command) throws IOException {
        if (channel != null) {
            OutputStream outputStream = channel.getOutputStream();
            outputStream.write(command.getBytes());
            outputStream.flush();
        }
    }
}
